package com.yuanmei.views;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.text.Html;
import android.text.Spanned;
import android.util.Log;
import android.util.Pair;
import android.view.View;
import android.view.Window;
import android.widget.ImageView;
import android.widget.TableLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.github.mikephil.charting.components.AxisBase;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.CandleEntry;
import com.github.mikephil.charting.formatter.IAxisValueFormatter;
import com.yuanmei.charts.BottomMarkerView;
import com.yuanmei.charts.HorizontalMarkerView;
import com.yuanmei.charts.KDJChartDataSet;
import com.yuanmei.charts.KLineChartDataSet;
import com.yuanmei.charts.GroupGestureListener;
import com.yuanmei.charts.CoupleValueSelectedListener;
import com.yuanmei.charts.LeftMarkerView;
import com.yuanmei.charts.MACDChartDataSet;
import com.yuanmei.charts.MarketIndexChartDataSet;
import com.yuanmei.charts.OBVChartDataSet;
import com.yuanmei.charts.RSIChartDataSet;
import com.yuanmei.charts.TaChartData;
import com.yuanmei.charts.TaCombinedChart;
import com.yuanmei.charts.VolumeChartDataSet;
import com.yuanmei.datas.DBUtils;
import com.yuanmei.datas.FetchDataService;
import com.yuanmei.datas.FetchKLineDaemon.KLineListener;
import com.yuanmei.charts.GroupGestureListener.OnDoubleTappedListener;
import com.yuanmei.charts.CoupleValueSelectedListener.OnDataIndexChangedListener;
import com.yuanmei.datas.FetchDataService.EmKLineType;
import com.yuanmei.model.HistoryDayQuote;
import com.yuanmei.model.HistoryHourQuote;
import com.yuanmei.model.HistoryMonthQuote;
import com.yuanmei.model.HistoryWeekQuote;
import com.yuanmei.model.KLineChartQuote;
import com.yuanmei.datas.TenCentWebSource;
import com.yuanmei.model.StockItem;
import com.yuanmei.model.TdxStockInfo;
import com.yuanmei.model.TradeCalenderRealm;
import com.yuanmei.common.Utils;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import io.realm.Realm;
import io.realm.RealmResults;
import io.realm.Sort;

public class KlineChartActivity extends Activity
        implements OnDataIndexChangedListener, OnDoubleTappedListener, KLineListener,
        QuickStockApplication.NotifyMessageCallback {
    private final static String TAG = KlineChartActivity.class.getName();
    protected static final int MSG_UPDATE_CHARTS = 1;
    protected static final int MSG_EMPTY_DATA = 2;
    protected static final int MSG_SHOW_TOAST = 3;

    // 开，收，高，低，量，换，额，查，比
    private TextView mOpen, mClose, mHigh, mLow,
            mOpenCloseChangePct, mTurnoverPercent, mTotalAsset,
            mAmount, mPriceToYCloseChangePercent,
            mTvStockName, mTvStockCode, mTvMatchDetail,
            mTvFinanceInfo, mTvFinancialStatements;

    private TaCombinedChart mKLineChart, mTaIndexChart;
    private TextView mKLineMaDetail, mTaIndexDetail;

    private HashMap<View, EmKLineType> mKLineViewTypes;
    private EmKLineType mCurrentKLineType = EmKLineType.DAY_LINE; // Default is dayline

    public void setCurrentKLineView(View clickView) {
        if (!mKLineViewTypes.containsKey(clickView))
            return;

        for (View view : mKLineViewTypes.keySet()) {
            if (view == clickView) {
                view.setBackgroundResource(R.color.selected_bg);
                mCurrentKLineType = mKLineViewTypes.get(view);
                mFetchBinder.startPriorUpdateHistory(mStockNetEaseCode,
                        mStockIsMarketIndex, mCurrentKLineType, KlineChartActivity.this);
            } else {
                view.setBackgroundResource(R.color.unselected_bg);
            }
        }
    }

    private void setMemberToTextViews() {
        mTvStockName.setText(mStockName);
        mTvStockCode.setText(mStockNetEaseCode.substring(1));
        mTvFinanceInfo.setText(mStockFinanceInfo);
        mTvMatchDetail.setText(mStockMatchDetail);
        mTvFinancialStatements.setText(mStockFinancialStatements);
    }

    private void initInfoViews() {
        TableLayout infoTable = findViewById(R.id.StockInfoTable);
        if (mStockIsMarketIndex) {
            infoTable.setColumnCollapsed(7, true);
            infoTable.setColumnCollapsed(8, true);
            infoTable.setColumnCollapsed(9, true);
            infoTable.setColumnCollapsed(10, true);
        }

        mTvStockName = findViewById(R.id.name);
        mTvStockCode = findViewById(R.id.code);
        mOpen = findViewById(R.id.open);
        mClose = findViewById(R.id.close);
        mHigh = findViewById(R.id.high);
        mLow = findViewById(R.id.low);
        mOpenCloseChangePct = findViewById(R.id.open_close_change_percent);
        mTurnoverPercent = findViewById(R.id.turnover_percent);
        mTotalAsset = findViewById(R.id.totalAsset);
        mAmount = findViewById(R.id.amount);
        mPriceToYCloseChangePercent = findViewById(R.id.price_to_yclose_change_percent);

        mKLineChart = findViewById(R.id.kline_chart_k);
        mTaIndexChart = findViewById(R.id.ta_index_chart);
        mKLineMaDetail = findViewById(R.id.ma_detail);

        mTvMatchDetail = findViewById(R.id.match_detail);
        mTvFinancialStatements = findViewById(R.id.finiancial_statments);
        mTvFinanceInfo = findViewById(R.id.finance_info);
        mTaIndexDetail = findViewById(R.id.ta_index_detail);

        ImageView ivSwitch = findViewById(R.id.ta_chart_switch);
        ivSwitch.setOnClickListener(view -> {
            roundAddTaIndex();

            updateTaChartData(mTaIndexChart, getCurrentTaChartData(), mSelectedDataIndex);
            Spanned desc = getCurrentTaChartData().getDescription(mSelectedDataIndex);
            mTaIndexDetail.setText(desc);
            resetSyncListener();
        });

        TextView timeShareTv = findViewById(R.id.tickchart_bt);
        timeShareTv.setOnClickListener(view -> startTickQuoteChartAcitivity());

        TextView webResourceTv = findViewById(R.id.eastmoney_webview_bt);
        webResourceTv.setOnClickListener(view -> startEastMoneyViewActivity());

        TextView blockViewTv = findViewById(R.id.block_view_bt);
        blockViewTv.setOnClickListener(view -> startBlockViewActivity());

        TextView qiMenViewButton = findViewById(R.id.Qimen_view_bt);
        qiMenViewButton.setOnClickListener(view -> startQimenAppActivity());

        TextView hourLine = findViewById(R.id.k_hour_bt);
        hourLine.setOnClickListener(this::setCurrentKLineView);
        TextView dayLine = findViewById(R.id.k_day_bt);
        dayLine.setOnClickListener(this::setCurrentKLineView);
        TextView weekLine = findViewById(R.id.k_week_bt);
        weekLine.setOnClickListener(this::setCurrentKLineView);
        TextView monthLine = findViewById(R.id.k_month_bt);
        monthLine.setOnClickListener(this::setCurrentKLineView);

        mKLineViewTypes = new HashMap<>();
        mKLineViewTypes.put(hourLine, EmKLineType.HOUR_LINE);
        mKLineViewTypes.put(dayLine, EmKLineType.DAY_LINE);
        mKLineViewTypes.put(weekLine, EmKLineType.WEEK_LINE);
        mKLineViewTypes.put(monthLine, EmKLineType.MONTH_LINE);

    }

    private void initCommonTaChartStyle(TaCombinedChart taChart) {
        taChart.setDrawBorders(true);
        taChart.setBorderWidth(1);

        taChart.setTouchEnabled(true);
        taChart.setDragEnabled(true);
        taChart.setScaleEnabled(true);
        taChart.setPinchZoom(true);
        taChart.setDoubleTapToZoomEnabled(false);

        taChart.setBorderColor(getResources().getColor(R.color.border_color));
        taChart.getDescription().setEnabled(false);
        taChart.setMinOffset(0f);
        taChart.setExtraOffsets(0f, 0f, 0f, 3f);
        taChart.getLegend().setEnabled(false);

        taChart.setDragDecelerationEnabled(true);
        taChart.setDragDecelerationFrictionCoef(0.2f);

        taChart.animateXY(1000, 1000);

        taChart.getAxisRight().setEnabled(false);

        XAxis xAxis = taChart.getXAxis();
        xAxis.setAxisMinimum(0f);

        YAxis leftYAxis = taChart.getAxisLeft();
        leftYAxis.setDrawGridLines(true);
        leftYAxis.setDrawAxisLine(false);
        leftYAxis.setDrawLabels(true);
        leftYAxis.enableGridDashedLine(10f, 10f, 0f);
        leftYAxis.setTextColor(getResources().getColor(R.color.text_color_common));
        leftYAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART);
        leftYAxis.setLabelCount(4, false);
        leftYAxis.setSpaceTop(4);//距离顶部留白

        LeftMarkerView leftMarkerView = new LeftMarkerView(this, R.layout.marker_text_view);
        BottomMarkerView bottomMarkerView = new BottomMarkerView(this, R.layout.marker_text_view);
        taChart.setLeftMarkerView(leftMarkerView);
        taChart.setBottomMarkerView(bottomMarkerView);
    }

    private void initKLineChart() {
        initCommonTaChartStyle(mKLineChart);
        //bar x y轴
        XAxis xAxis = mKLineChart.getXAxis();
        xAxis.setDrawLabels(true);
        xAxis.setDrawGridLines(false);//是否显示X坐标轴上的刻度竖线，默认是true
        xAxis.setDrawAxisLine(false); //是否绘制坐标轴的线，即含有坐标的那条线，默认是true
        xAxis.enableGridDashedLine(10f, 10f, 0f);
        xAxis.setTextColor(getResources().getColor(R.color.text_color_common));//设置字的颜色
        xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);//设置值显示在什么位置
        xAxis.setAvoidFirstLastClipping(true);//设置首尾的值是否自动调整，避免被遮挡

        HorizontalMarkerView hMarkerView = new HorizontalMarkerView(this, R.layout.marker_line_view);
        mKLineChart.setHorizontalMarkerView(hMarkerView);
    }

    /**
     * 初始化下面的TA-CHARTS
     */
    private void initTaCharts() {
        initCommonTaChartStyle(mTaIndexChart);
        XAxis xAxis = mTaIndexChart.getXAxis();
        xAxis.setDrawLabels(true);
        xAxis.setDrawGridLines(false);//是否显示X坐标轴上的刻度竖线，默认是true
        xAxis.setDrawAxisLine(false); //是否绘制坐标轴的线，即含有坐标的那条线，默认是true
        xAxis.enableGridDashedLine(10f, 10f, 0f);
        xAxis.setTextColor(getResources().getColor(R.color.text_color_common));//设置字的颜色
        xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);//设置值显示在什么位置
        xAxis.setAvoidFirstLastClipping(true);//设置首尾的值是否自动调整，避免被遮挡

        YAxis leftYAxis = mTaIndexChart.getAxisLeft();
        leftYAxis.setLabelCount(1, true);
        leftYAxis.setDrawZeroLine(true); // draw a zero line
        leftYAxis.setZeroLineColor(Color.GRAY);
        leftYAxis.setZeroLineWidth(0.7f);
        leftYAxis.setDrawLabels(false);
        mTaIndexChart.getXAxis().setEnabled(false);
        mTaIndexChart.getDescription().setEnabled(false);
    }

    private void updateTextViewToXIndex(int index) {
        mTvStockName.setText(mStockName);
        mTvStockCode.setText(mStockNetEaseCode.substring(1));
        mTvFinanceInfo.setText(mStockFinanceInfo);
        mTvMatchDetail.setText(mStockMatchDetail);
        mTvFinancialStatements.setText(mStockFinancialStatements);
        if (index >= 0 && index < mKLineChartData.getXAxisSize()) {
            int upColor = getResources().getColor(R.color.increasing_color);
            int downColor = getResources().getColor(R.color.decreasing_color);

            CandleEntry entry = mKLineChartData.getCandleEntry(index);
            float open = entry.getOpen();
            mOpen.setText(Utils.priceToDecimalString(open));

            float yestoday_close = open;
            if (index > 0) {
                yestoday_close = mKLineChartData.getClose(index - 1);
            }

            mOpen.setTextColor((open > yestoday_close) ? upColor : downColor);
            float close = entry.getClose();
            mClose.setText(Utils.priceToDecimalString(close));
            mClose.setTextColor((close > open) ? upColor : downColor);
            float high = entry.getHigh();
            mHigh.setText(Utils.priceToDecimalString(high));
            mHigh.setTextColor((high > yestoday_close) ? upColor : downColor);
            float low = entry.getLow();
            mLow.setText(Utils.priceToDecimalString(low));
            mLow.setTextColor((low > yestoday_close) ? upColor : downColor);
            float chgPct1 = (close - open) * 100 / open;
            mOpenCloseChangePct.setText(Utils.percentToDecimalString(chgPct1));
            mOpenCloseChangePct.setTextColor(chgPct1 > 0.0f ? upColor : downColor);

            float chgPct2 = (close - yestoday_close) * 100 / yestoday_close;
            mPriceToYCloseChangePercent.setText(Utils.percentToDecimalString(chgPct2));
            mPriceToYCloseChangePercent.setTextColor(chgPct2 > 0.0f ? upColor : downColor);

            float volume = mKLineChartData.getVolume(index);
//            mVolume.setText(Utils.volumeToShortUnit(volume));
            float amount = mKLineChartData.getAmount(index);
            if (amount == 0.0f) {
                amount = volume * close;
            }
            mAmount.setText(Utils.amountToShortUnit(amount));

            float outstandingShares = 0.0f;
            if (mStockBasicInformation != null) {
                outstandingShares = mStockBasicInformation.getOutstanding();
                outstandingShares = outstandingShares * 10000 * 10000;
//                if (totalShares < 1000) {
//                    totalShares = totalShares * 10000 * 10000;
//                } else {
//                    totalShares = totalShares * 10000;
//                }
            }

            float totalMarketValue = outstandingShares * close;
            mTotalAsset.setText(Utils.amountToShortUnit(totalMarketValue));

            float to_pct = mKLineChartData.getTurnOver(index);
            if (to_pct == 0.0f &&
                    outstandingShares > 0.0f &&
                    volume > 0.0f) {
                to_pct = (volume / outstandingShares)  * 100; // % pct
            }
            mTurnoverPercent.setText(Utils.percentToDecimalString(to_pct));

            String ma3 = mKLineChartData.getCloseSma3(index);
            String ma5 = mKLineChartData.getCloseSma5(index);
            String ma10 = mKLineChartData.getCloseSma10(index);
            String ma20 = mKLineChartData.getCloseSma20(index);
            String ma30 = mKLineChartData.getCloseSma30(index);
            String ma60 = mKLineChartData.getCloseSma60(index);
            String ma120 = mKLineChartData.getCloseSma120(index);
            String ma240 = mKLineChartData.getCloseSma240(index);

            String maHtmlValue = String.format(
                    "<font color='#8383EB'>3:%s</font> " +
                    "<font color='#bbbbbb'>5:%s</font> " +
                    "<font color='#E7B448'>10:%s</font> " +
                    "<font color='#FF0000'>20:%s</font> " +
                    "<font color='#00ff26'>30:%s</font> " +
                    "<font color='#00D0FC'>60:%s</font> " +
                    "<font color='#9467BD'>120:%s</font> " +
                    "<font color='#8C564B'>240:%s</font>",
                    ma3, ma5, ma10, ma20, ma30, ma60, ma120, ma240);
            Spanned maValues = Html.fromHtml(maHtmlValue);
            mKLineMaDetail.setText(maValues);

            mTaIndexDetail.setText(getCurrentTaChartData().getDescription(mSelectedDataIndex));
        }
    }

    private int mSelectedDataIndex = -1;

    public void onSelectedDataIndexChanged(int index) {
        mSelectedDataIndex = index;
        updateTextViewToXIndex(index);
        Log.d(TAG, "JKP mSelectedDataIndex=" + mSelectedDataIndex);
    }

    private void startTickQuoteChartAcitivity() {
        Intent intent = new Intent(KlineChartActivity.this,
                TickQuoteChartActivity.class);
        intent.putExtra("Name", mStockName);
        intent.putExtra("NetEaseCode", mStockNetEaseCode);
        intent.putExtra("IsMarketIndex", mStockIsMarketIndex);
        intent.putExtra("XDate",
                mKLineChartData.getIDate(mSelectedDataIndex));
        intent.putExtra("TodayBuys", mTodayBuys);
        intent.putExtra("TodaySells", mTodaySells);
        if (mSelectedDataIndex > 0) {
            intent.putExtra("YesTodayClose", mKLineChartData.getClose(mSelectedDataIndex - 1));
        } else {
            intent.putExtra("YesTodayClose", mKLineChartData.getOpen(mSelectedDataIndex));
        }

        startActivity(intent);
    }

    private void startEastMoneyViewActivity() {
        Intent intent = new Intent(KlineChartActivity.this,
                EastMoneyViewActivity.class);
        intent.putExtra("Name", mStockName);
        intent.putExtra("NetEaseCode", mStockNetEaseCode);
        intent.putExtra("IsMarketIndex", mStockIsMarketIndex);
        startActivity(intent);
    }

    private void startBlockViewActivity() {
        if (mStockBasicInformation == null) {
            if (mStockIsMarketIndex) {
                Toast.makeText(this,
                        mStockName + "(" + mStockNetEaseCode.substring(1) + ") 是股票指数, 无法显示板块列表",
                        Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this,
                        mStockName + "(" + mStockNetEaseCode.substring(1) + ") 没有股票基本信息，无法显示板块列表",
                        Toast.LENGTH_SHORT).show();
            }
            return;
        }
        
        Intent intent = new Intent(KlineChartActivity.this,
                BlockViewActivity.class);
        intent.putExtra("Name", mStockName);
        intent.putExtra("NetEaseCode", mStockNetEaseCode);
        if (mStockBasicInformation != null) {
            intent.putExtra("Industry", mStockBasicInformation.getIndustry());
            intent.putExtra("Area", mStockBasicInformation.getArea());
        }
        startActivity(intent);
    }

    private void startQimenAppActivity() {
        // 启动 奇门学习 WebView
//                Intent intent = new Intent(KlineChartActivity.this,
//                        QiMenWebViewActivity.class);
//                intent.putExtra("Name", mStockName);
//                intent.putExtra("NetEaseCode", mStockNetEaseCode);
//                intent.putExtra("IsMarketIndex", mStockIsMarketIndex);
//                int timeToMarket = 19700101;
//                if(mStockBasicInformation != null) {
//                    timeToMarket = mStockBasicInformation.getTimeToMarket();
//                }
//                intent.putExtra("TimeToMarket", timeToMarket);
//                startActivity(intent);


        //启动 奇门学习 应用

//        Intent intent = new Intent(Intent.ACTION_VIEW);
//        intent.addCategory(Intent.CATEGORY_LAUNCHER);
//        ComponentName cn = new ComponentName("com.yuanmei.qmstudy",
//                "com.yuanmei.qmstudy.QiMenMainActivity");
//        intent.setComponent(cn);
//        int timeToMarket = 19700101;
//        if (mStockBasicInformation != null) {
//            timeToMarket = mStockBasicInformation.getTimeToMarket();
//        }
//
//        intent.putExtra("StockName", mStockName);
//        intent.putExtra("StockCode", mStockNetEaseCode.substring(1));
//        intent.putExtra("StockTimeToMarket", timeToMarket);
//        int date = mKLineChartData.getIDate(mSelectedDataIndex);
//        if (date <= 0) {
//            date = 0;
//        }
//        intent.putExtra("StockDate", date);
//        startActivity(intent);

        Toast.makeText(this,
                "奇门功能已关闭",
                Toast.LENGTH_SHORT).show();
    }

    public void onDoubleTapped(int index) {
        mSelectedDataIndex = index;
        updateTextViewToXIndex(index);

        if (mStockIsMarketIndex) {
            Toast.makeText(this,
                    mStockName + "(" + mStockNetEaseCode.substring(1) + ") 是股票指数, 没有分时数据",
                    Toast.LENGTH_SHORT).show();
            return;
        }
        startTickQuoteChartAcitivity();
    }

    private void resetSyncListener() {
        // Gesture 同步
        mKLineChart.setOnChartGestureListener(new GroupGestureListener(mKLineChart, new TaCombinedChart[]{mTaIndexChart}, this));
        mTaIndexChart.setOnChartGestureListener(new GroupGestureListener(mTaIndexChart, new TaCombinedChart[]{mKLineChart}, this));

        // 高亮选择同步
        mKLineChart.setOnChartValueSelectedListener(new CoupleValueSelectedListener(mKLineChart, mTaIndexChart, this));
        mTaIndexChart.setOnChartValueSelectedListener(new CoupleValueSelectedListener(mTaIndexChart, mKLineChart, this));
    }

    @Override
    public void onToastNotify(String notifyMessage) {
        Message msg = Message.obtain(mHandler, MSG_SHOW_TOAST);
        msg.obj = notifyMessage;
        msg.sendToTarget();
    }

    @Override
    public void onUpdateProgressNotify(int progressPercent) {

    }

    @Override
    public void onShowProgressBar() {

    }

    @Override
    public void onHideProgressBar() {

    }

    public static class DateAxisFormatter implements IAxisValueFormatter {
        List<String> mDateValues;

        DateAxisFormatter(List<String> iDates) {
            this.mDateValues = iDates;
        }

        @Override
        public String getFormattedValue(float value, AxisBase axis) {
            int i = (int) value % mDateValues.size();
            if (i < 0)
                return "";
            return mDateValues.get(i);
        }
    }

    private String mStockName;
    private String mStockNetEaseCode;
    private String mStockFinanceInfo;
    private String mStockMatchDetail;
    private String mStockFinancialStatements;
    private boolean mStockIsMarketIndex;
    private FetchDataService.FetchDataBinder mFetchBinder;

    private void updateTaChartData(TaCombinedChart taChart, TaChartData taData, int viewX) {
        taChart.setData(taData.getTaChartData());
        taChart.setTaChartData(taData);
        taChart.setAutoScaleMinMaxEnabled(true);
        List<String> xLabels = taData.getXAxisLabelList();
        if (xLabels != null)
            taChart.getXAxis().setValueFormatter(new DateAxisFormatter(xLabels));
        taChart.getXAxis().setAxisMinimum(-0.5f);
        taChart.getXAxis().setAxisMaximum(taData.getXAxisSize() + 0.5f);
        taChart.setVisibleXRangeMaximum(35);
        taChart.moveViewToX(viewX);
    }

    private void refreshCharts() {
        if (mSelectedDataIndex < 0 ||
                mSelectedDataIndex >= mKLineChartData.getXAxisSize()) {
            mSelectedDataIndex = mKLineChartData.getXAxisSize() - 1;
        }

        updateTaChartData(mKLineChart, mKLineChartData, mSelectedDataIndex);
        updateTaChartData(mTaIndexChart, getCurrentTaChartData(), mSelectedDataIndex);
        updateTextViewToXIndex(mSelectedDataIndex);
        resetSyncListener();
//        mHandler.postDelayed(()->{
//            Log.i(TAG, "Delay 1s to move view to x");
//            mKLineChart.moveViewToX(mSelectedDataIndex);
//            mTaIndexChart.moveViewToX(mSelectedDataIndex);
//        },  1000);
    }

    private static class KlineMessageHandler extends Handler {
        private final WeakReference<KlineChartActivity> mWeakKlineActivity;

        private KlineMessageHandler(KlineChartActivity klActivity) {
            mWeakKlineActivity = new WeakReference<>(klActivity);
        }

        @Override
        public void handleMessage(Message msg) {
            KlineChartActivity klActivity = mWeakKlineActivity.get();
            if (klActivity == null)
                return;

            switch (msg.what) {
                case MSG_UPDATE_CHARTS:
                    klActivity.refreshCharts();
                    break;
                case MSG_EMPTY_DATA:
                    Toast.makeText(klActivity,
                            klActivity.mStockNetEaseCode + "'s data is NULL", Toast.LENGTH_SHORT).show();
                    break;
                case MSG_SHOW_TOAST:
                    Toast.makeText(klActivity, (String) msg.obj, Toast.LENGTH_LONG).show();
                    break;
                default:
                    break;
            }
        }
    }

    private KlineMessageHandler mHandler;
    private HistoryDayQuote mRealTimeDayQuote;
    private TdxStockInfo mStockBasicInformation;

    private String mTodayBuys, mTodaySells;

    private void initMemberByIntent(Intent intent) {
        mStockName = intent.getStringExtra("Name");
        mStockNetEaseCode = intent.getStringExtra("NetEaseCode");
        mStockFinanceInfo = intent.getStringExtra("FinanceInfo");
        mStockMatchDetail = intent.getStringExtra("MatchDetail");
        mStockFinancialStatements = intent.getStringExtra("FinancialStatements");
        mTodayBuys  = intent.getStringExtra("TodayBuys");
        mTodaySells = intent.getStringExtra("TodaySells");
        mStockIsMarketIndex = intent.getBooleanExtra("IsMarketIndex", false);
        String code = mStockNetEaseCode.substring(1);
        if (!mStockIsMarketIndex) {
            mStockBasicInformation = DBUtils.getTdxStockInfoInstance(code);
        }

        float todayOpen = intent.getFloatExtra("TodayOpen", 0);
        float todayPrice = intent.getFloatExtra("TodayPrice", 0);
        float todayHigh = intent.getFloatExtra("TodayHigh", 0);
        float todayLow = intent.getFloatExtra("TodayLow", 0);
        float todayYClose = intent.getFloatExtra("TodayYClose", 0);
        float todayVolume = intent.getFloatExtra("TodayVolume", 0);
        float todayAmount = intent.getFloatExtra("TodayAmount", 0);
        int todayIDate = intent.getIntExtra("TodayIDate", 0);
        float todayPchg = (todayPrice - todayYClose) * 100 / todayYClose;
        float turnover = 0;
        if (mStockBasicInformation != null)
            turnover = (todayVolume * 100) / (mStockBasicInformation.getOutstanding() * 10000 * 10000);

        mRealTimeDayQuote = new HistoryDayQuote(todayIDate, todayOpen, todayHigh, todayLow, todayPrice, todayVolume, todayPchg, todayAmount, turnover);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initMemberByIntent(getIntent());
        requestWindowFeature(Window.FEATURE_NO_TITLE);
//        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
//                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_kline);

        initInfoViews();
        initKLineChart();
        initTaCharts();
        setMemberToTextViews();
        mHandler = new KlineMessageHandler(this);
        mFetchServiceConnection = new FetchServiceConnection();
        bindService(new Intent(this, FetchDataService.class),
                mFetchServiceConnection, BIND_AUTO_CREATE);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        initMemberByIntent(intent);
        setMemberToTextViews();
        if (mFetchBinder != null)
            mFetchBinder.startPriorUpdateHistory(mStockNetEaseCode,
                    mStockIsMarketIndex, mCurrentKLineType, KlineChartActivity.this);
        super.onNewIntent(intent);
    }

    private KLineChartDataSet mKLineChartData;

    private int mTaChartCurrentIndex = 0;
    private final List<TaChartData> mTaChartDataList = new ArrayList<>();

    private void roundAddTaIndex() {
        if (mTaChartDataList.size() > 0) {
            mTaChartCurrentIndex = (++mTaChartCurrentIndex) % mTaChartDataList.size();
        } else {
            mTaChartCurrentIndex = 0;
        }
    }

    private TaChartData getCurrentTaChartData() {
        return mTaChartDataList.get(mTaChartCurrentIndex);
    }

    private class FetchServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "onServiceConnected enter");
            mFetchBinder = (FetchDataService.FetchDataBinder) service;
            mFetchBinder.startPriorUpdateHistory(mStockNetEaseCode,
                    mStockIsMarketIndex, mCurrentKLineType, KlineChartActivity.this);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mFetchBinder = null;
        }
    }

    private FetchServiceConnection mFetchServiceConnection;

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mFetchServiceConnection != null) {
            unbindService(mFetchServiceConnection);
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        QuickStockApplication.registerNotifyCallback(this);
    }

    @Override
    public void onPause() {
        super.onPause();
        QuickStockApplication.unregisterNotifyCallback(this);
    }

    /**
     * Realm 常见操作
     * sum()：对指定字段求和。
     * average()：对指定字段求平均值。
     * min(): 对指定字段求最小值。
     * max() : 对指定字段求最大值。count : 求结果集的记录数量。
     * findAll(): 返回结果集所有字段，返回值为RealmResults队列
     * between(), greaterThan(),lessThan(), greaterThanOrEqualTo() & lessThanOrEqualTo()
     * equalTo() & notEqualTo()
     * contains(), beginsWith() & endsWith()
     * isNull() & isNotNull()
     * isEmpty()& isNotEmpty()
     */
    private static final int SHOW_LINE_COUNT = 400;
    private static final int FETCH_MAX_COUNT = SHOW_LINE_COUNT + 260;

    List<KLineChartQuote> getKLineQuotesOfDay(Realm realm) {
        RealmResults<HistoryDayQuote> results =
                realm.where(HistoryDayQuote.class)
                        .sort("date", Sort.ASCENDING).findAll();
        if (results.size() <= 0) {
            return null;
        }
        int start_idx = Math.max(results.size() - FETCH_MAX_COUNT, 0);
        List<HistoryDayQuote> dayLineList = realm.copyFromRealm(results.subList(start_idx, results.size()));
        Number maxDate = results.max("date");
        if (maxDate == null)
            return null;
        try {
            int lastRecordIDate = maxDate.intValue();
            Date lastRecordDate = Utils.getDateFromIDate(lastRecordIDate);
            Date needFillEndDate = new Date(Utils.getLastTradeDate().getTime() - Utils.MILLIS_PER_DAY);
            int needFillEndIDate = Utils.getIDateFromDate(needFillEndDate);
            if (lastRecordIDate < needFillEndIDate) {
                Date start = new Date(lastRecordDate.getTime() + Utils.MILLIS_PER_DAY);
                List<HistoryDayQuote> lds = TenCentWebSource.getHistoryDayQuotes(mStockNetEaseCode,
                        mStockBasicInformation == null ? 0 : mStockBasicInformation.getOutstanding(),
                        start, needFillEndDate);

                if (lds != null && lds.size() > 0) {
                    for (HistoryDayQuote ndq : lds) {
                        Date qDate = Utils.sDefaultDateFormat.parse(ndq.getQuoteDate());
                        if ((qDate.getTime() - lastRecordDate.getTime()) >= Utils.MILLIS_PER_DAY) {
                            dayLineList.add(ndq);
                        }
                    }
                }
            }

            if (mRealTimeDayQuote.isQualified()
                    && TradeCalenderRealm.isOpenDate(mRealTimeDayQuote.getDate())
                    && lastRecordIDate < mRealTimeDayQuote.getDate()) {
                if (mStockBasicInformation != null && mStockBasicInformation.getOutstanding() > 0) {
                    float turnOver = (mRealTimeDayQuote.getVolume() * 100) /
                            (mStockBasicInformation.getOutstanding() * 10000 * 10000);
                    mRealTimeDayQuote.setTurnover(turnOver);
                }
                dayLineList.add(mRealTimeDayQuote);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Arrays.asList(dayLineList.toArray(new KLineChartQuote[0]));
    }

    List<KLineChartQuote> getKLineQuotesOfWeek(Realm realm) {
        RealmResults<HistoryWeekQuote> results =
                realm.where(HistoryWeekQuote.class)
                        .sort("date", Sort.ASCENDING).findAll();
        if (results.size() <= 0) {
            return null;
        }

        int start_idx = Math.max(results.size() - FETCH_MAX_COUNT, 0);
        List<HistoryWeekQuote> weekLineList = realm.copyFromRealm(results.subList(start_idx, results.size()));
        try {
            HistoryWeekQuote lwq = results.get(results.size() - 1);
            if (lwq != null) {
                Date ld = Utils.sDefaultDateFormat.parse(lwq.getQuoteDate());
                Date end = Utils.getLastTradeDate();
                long delta = end.getTime() - ld.getTime();
                if (delta > Utils.MILLIS_PER_DAY) {
                    Date start = new Date(ld.getTime() + Utils.MILLIS_PER_DAY);
                    List<HistoryWeekQuote> lws =
                            TenCentWebSource.getHistoryWeekQuotes(mStockNetEaseCode,
                                    start, end);
                    if (lws != null && lws.size() > 0) {
                        for (HistoryWeekQuote nwq : lws) {
                            Date qDate = Utils.sDefaultDateFormat.parse(nwq.getQuoteDate());
                            if ((qDate.getTime() - ld.getTime()) >= Utils.MILLIS_PER_DAY) {
                                weekLineList.add(nwq);
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "Append real time week line fail:", e);
        }

        return Arrays.asList(weekLineList.toArray(new KLineChartQuote[0]));
    }

    List<KLineChartQuote> getKLineQuotesOfMonth(Realm realm) {
        RealmResults<HistoryMonthQuote> results =
                realm.where(HistoryMonthQuote.class)
                        .sort("date", Sort.ASCENDING).findAll();
        if (results.size() <= 0) {
            return null;
        }

        int start_idx = Math.max(results.size() - FETCH_MAX_COUNT, 0);
        List<HistoryMonthQuote> monthLineList = realm.copyFromRealm(results.subList(start_idx, results.size()));
        try {
            HistoryMonthQuote lmq = results.get(results.size() - 1);
            if (lmq != null) {
                Date ld = Utils.sDefaultDateFormat.parse(lmq.getQuoteDate());
                Date end = Utils.getLastTradeDate();
                long delta = end.getTime() - ld.getTime();
                if (delta > Utils.MILLIS_PER_DAY) {
                    Date start = new Date(ld.getTime() + Utils.MILLIS_PER_DAY);
                    List<HistoryMonthQuote> nmqs =
                            TenCentWebSource.getHistoryMonthQuotes(mStockNetEaseCode,
                                    start, end);
                    if (nmqs != null && nmqs.size() > 0) {
                        for (HistoryMonthQuote nmq : nmqs) {
                            Date qDate = Utils.sDefaultDateFormat.parse(nmq.getQuoteDate());
                            if ((qDate.getTime() - ld.getTime()) >= Utils.MILLIS_PER_DAY) {
                                monthLineList.add(nmq);
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "Append real time month line fail:", e);
        }

        return Arrays.asList(monthLineList.toArray(new KLineChartQuote[0]));
    }

    @SuppressWarnings("Deprecated")
    List<KLineChartQuote> getKLineQuotesOfHour(Realm realm) {
        RealmResults<HistoryHourQuote> results =
                realm.where(HistoryHourQuote.class)
                        .sort("date", Sort.ASCENDING).findAll();
        if (results.size() <= 0) {
            return null;
        }
        int start_idx = Math.max(results.size() - FETCH_MAX_COUNT, 0);
        List<HistoryHourQuote> hourLineList = realm.copyFromRealm(results.subList(start_idx, results.size()));

        try {
            HistoryHourQuote lq = results.get(results.size() - 1);
            if (lq != null) {
                String lds = lq.getQuoteDate();
                Date ld = Utils.sDateMinuteFormat.parse(lds);
                Date end = Utils.getLastTradeDate();
                long delta = end.getTime() - ld.getTime();
                if (delta >= Utils.MILLIS_PER_DAY || ld.getHours() < 15) {
                    Date start = new Date(ld.getTime() + Utils.MILLIS_PER_HOUR);
                    List<HistoryHourQuote> lhs =
                            TenCentWebSource.getHistoryHourQuotes(mStockNetEaseCode, start, end);
                    if (lhs != null && lhs.size() > 0) {
                        for (HistoryHourQuote hq : lhs) {
                            Date qd = Utils.sDateMinuteFormat.parse(hq.getDate());
                            if (qd.getTime() > ld.getTime()) {
                                hourLineList.add(hq);
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "Append real time week line fail:", e);
        }

        return Arrays.asList(hourLineList.toArray(new KLineChartQuote[0]));
    }

    @Override
    public void onLineFetched(String neCode, Realm realm, EmKLineType type) {
        if (!neCode.endsWith(mStockNetEaseCode)) {
            return;
        }
        Log.d(TAG, "Stock(" + neCode + ") onLineFetched type=" + type);
        List<KLineChartQuote> kqList = null;
        long startTime = System.nanoTime();
        if (type == EmKLineType.DAY_LINE) {
            kqList = getKLineQuotesOfDay(realm);
        } else if (type == EmKLineType.WEEK_LINE) {
            kqList = getKLineQuotesOfWeek(realm);
        } else if (type == EmKLineType.HOUR_LINE) {
            kqList = getKLineQuotesOfHour(realm);
        } else if (type == EmKLineType.MONTH_LINE) {
            kqList = getKLineQuotesOfMonth(realm);
        }
        if (kqList == null) {
            Log.d(TAG, "Stock(" + neCode + ") has empty lines");
            Message.obtain(mHandler, MSG_EMPTY_DATA).sendToTarget();
            return;
        }
        mKLineChartData = new KLineChartDataSet(this, kqList, SHOW_LINE_COUNT);
        mTaChartDataList.clear();
        mTaChartDataList.add(new VolumeChartDataSet(this, mKLineChartData));
        mTaChartCurrentIndex = 0;

        new Thread(() -> {
            mTaChartDataList.add(new MACDChartDataSet(KlineChartActivity.this, mKLineChartData));
            mTaChartDataList.add(new KDJChartDataSet(KlineChartActivity.this, mKLineChartData));
            mTaChartDataList.add(new OBVChartDataSet(KlineChartActivity.this, mKLineChartData));
            mTaChartDataList.add(new RSIChartDataSet(KlineChartActivity.this, mKLineChartData));

            List<Pair<String, String>> marketIndex = new ArrayList<>();
            marketIndex.add(new Pair<>("000001", "上证指数"));
            marketIndex.add(new Pair<>("399106", "深证综指"));
            marketIndex.add(new Pair<>("399606", "创业板R"));
            marketIndex.add(new Pair<>("399005", "中小板指"));
            for (Pair pair: marketIndex) {
                String miCode = pair.first.toString();
                String miName = pair.second.toString();
                String neSZCode = StockItem.getNeCodeByMarketIndexCode(miCode);
                List<KLineChartQuote> szDayQuotes = DBUtils.getIndexDayQuotesByIDates(neSZCode, mKLineChartData.getIDateList());
                if (null == szDayQuotes) {
                    QuickStockApplication.notifyToast(mStockName + "匹配" + miName +"数据失败");
                } else {
                    mTaChartDataList.add(new MarketIndexChartDataSet(KlineChartActivity.this, szDayQuotes, miName));
                }
            }

        }).start();

        long endTime = System.nanoTime();
        Log.i(TAG, "Show neCode=" + neCode + " usedTimes="
                + (endTime - startTime) / 1000000 + ", dataCount=" + kqList.size());
        Message.obtain(mHandler, MSG_UPDATE_CHARTS).sendToTarget();
    }
}
